from twisted.internet import reactor, defer, protocol
from SmallProtocol_frm import SmallProtocol_framer

import struct
from collections import deque

#---------------------------------------------------------------------------#
# Logging
#---------------------------------------------------------------------------#
from twisted.python import log

class eDIP43A_Async(protocol.Protocol):
   """
   """
   __tid = 0

   def __init__(self):
      ''' Initializes the framer module

      :param framer: The framer to use for the protocol
      '''
      self.framer = SmallProtocol_framer() 
      self._requests = deque() # link queue to tid
      self._connected = False
      log.msg("Class initialized")

   def connectionMade(self):
      ''' Called upon a successful client connection.
      '''
      log.msg("Client connected to display")
      self._connected = True

   def connectionLost(self, reason):
      ''' Called upon a client disconnect

      :param reason: The reason for the disconnect
      '''
      log.msg("Client disconnected from modbus server: %s" % reason)
      self._connected = False

   def dataReceived(self, data):
      ''' Get response, check for valid message, decode result

      :param data: The data returned from the server
      '''
      #log.msg(data)
      self.framer.processIncomingPacket(data, self._callback)

   def execute(self, request, prot):
      ''' Starts the producer to send the next request to
      consumer.write(Frame(request))
      '''
      packet = self.framer.buildPacket(request, prot)
      self.transport.write(packet)
      return self._buildResponse()

   def _callback(self, reply):
      ''' The callback to call with the response message

      :param reply: The decoded response message
      '''
      # todo errback/callback
      if self._requests:
         self._requests.popleft().callback(reply)

   def _buildResponse(self):
      ''' Helper method to return a deferred response
      for the current request.

      :returns: A defer linked to the latest request
      '''
      if not self._connected:
         return defer.fail(ConnectionException('Client is not connected'))

      d = defer.Deferred()
      self._requests.append(d)
      return d


